package com.iot08.api.service;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.utils.StringUtils;
import com.iot08.api.config.properties.ListDataProperties;
import com.iot08.api.config.redis.RedisFilterService;
import com.iot08.api.constant.Constant;
import com.iot08.api.response.ChatMessageLastResp;
import com.iot08.api.service.supplement.KafkaService;
import com.iot08.api.utils.buckettool.BaseBucketTool;
import com.iot08.api.utils.ContentSecurityUtil;
import com.iot08.api.utils.request.ParamHandle;
import com.iot08.api.vo.request.*;
import com.iot08.api.vo.request.base.BaseAccountReqVO;
import com.iot08.common.config.mongodb.query.UpdateVO;
import com.iot08.common.constant.Const;
import com.iot08.common.constant.ConstKafka;
import com.iot08.common.entity.app.*;
import com.iot08.common.service.base.BaseService;
import com.iot08.common.utils.*;
import com.iot08.common.utils.response.ResponseEnum;
import com.iot08.common.utils.response.ResponseMap;
import com.mongodb.client.FindIterable;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * @version 1.0
 * @Description:
 * @author: Rock
 * @time: 2020/11/4 11:51
 */
@Slf4j
@Service
public class ChatGroupService extends BaseService<ChatGroup> {

    @Resource
    private AccountService accountService;
    @Resource
    private DeviceService deviceService;
    @Resource
    private BaseBucketTool baseBucketTool;

    @Resource
    private ContentSecurityUtil contentSecurityUtil;

    @Resource
    private KafkaService kafkaService;
    @Resource
    private ListDataProperties listDataProperties;
    @Resource
    private ChatGroupMessageService chatGroupMessageService;

    @Resource
    private UserKeyActionService userKeyActionService;

    @Resource
    private RedisFilterService redisFilterService;
    @Resource
    private SystemLimitService systemLimitService;
    @Resource
    private VipMemberService vipMemberService;
    @Resource
    private SensitivityFileGroupMessageService sensitivityFileGroupMessageService;

    @Value("${domain.name}")
    private String domainName;

    @Value("${app.downloadURL}")
    private String appDownloadURL;

    /**
     * 根据设备获取对应的 群组对象
     * @param device
     * @time: 2020/11/4 14:26
     * @author: Rock
     * @version 1.0
     */
    public ChatGroup getChatGroupByDevice(Device device) {
        if (CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
            String groupInfoId = device.getChatGroupMap().keySet().iterator().next();
            if (ObjectId.isValid(groupInfoId)) {
                return findById(new ObjectId(groupInfoId));
            }
        }
        return null;
    }

    /**
     * @param accountId
     * @time: 2021/1/6 16:33
     * @author: Rock
     * @version 1.0
     */
    public ChatGroup createByAccountId(String accountId) {
        ChatGroup group = new ChatGroup();
        group.setCreator(accountId);
        Double time = DateUtil.nowTimestampToDouble();
        group.setTimestamp(time);
        group.setIt(RandomUtil.getRandom(100000, 999999));
        Map<String, Map<String, Object>> m = new HashMap<>();
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", time);
        sub.put("k", "admin");
        m.put(accountId, sub);
        group.setMemberMap(m);
        return this.save(group);
    }

    /**
     * @param chatGroup
     * @param account
     * @time: 2020/11/4 15:38
     * @author: Rock
     * @version 1.0
     */
    public void addAccount(ChatGroup chatGroup, Account account) {
        if (chatGroup.getMemberMap() == null) {
            chatGroup.setMemberMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", DateUtil.nowTimestampToLong());
        chatGroup.getMemberMap().put(account.getId(), sub);
        Update update = new Update();
        update.set("m", chatGroup.getMemberMap());
        this.updateById(chatGroup.getId(), update);
        // 发送kafka
        kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId(), null);
    }

    /**
     * @param chatGroup
     * @param device
     * @time: 2020/11/4 15:47
     * @author: Rock
     * @version 1.0
     */
    public void addDevice(ChatGroup chatGroup, Device device) {
        if (chatGroup.getDeviceMap() == null) {
            chatGroup.setDeviceMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", DateUtil.nowTimestampToLong());
        sub.put("v", device.getVendor());
        chatGroup.getDeviceMap().put(device.getId(), sub);
        Update update = new Update();
        update.set("d", chatGroup.getDeviceMap());
        this.updateById(chatGroup.getId(), update);
    }

    /**
     * @param openid
     * @param ids
     * @time: 2020/11/9 16:11
     * @version 1.0
     * @author: Rock
     */
    public void delAccountHandle(String openid, Set<ObjectId> ids) {
        List<ChatGroup> chatGroups = this.listByIds(ids);
        for (ChatGroup cg : chatGroups) {
            if (CollectionUtil.removeMapKey(cg.getMemberMap(), openid)) {
                UpdateVO update = new UpdateVO();
                update.setMap("m", cg.getMemberMap());
                this.updateById(new ObjectId(cg.getId()), update);

                //发送kafka
                Account account = accountService.findById(openid);
                kafkaService.sendPushToChatGroup(cg, account, ConstKafka.CHAT_GROUP_REMOVE, cg.getId(), null);
            }
        }
    }

    /**
     * @param chatGroup
     * @param deviceId
     * @param deviceV
     * @time: 2020/11/12 17:47
     * @author: Rock
     * @version 1.0
     */
    public void addDeviceHandle(ChatGroup chatGroup, String deviceId, Integer deviceV, UpdateVO updateChatGroup) {
        if (chatGroup.getDeviceMap() == null) {
            chatGroup.setDeviceMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", System.currentTimeMillis() / 1000d);
        sub.put("v", deviceV == null ? Const.VENDER_20000 : deviceV);
        chatGroup.getDeviceMap().put(deviceId, sub);
        updateChatGroup.setMap("d", chatGroup.getDeviceMap());
    }

    /**
     * @param chatGroup
     * @param imei
     * @time: 2021/2/2 16:42
     * @author: Rock
     * @version 1.0
     */
    public void delDeviceHandle(ChatGroup chatGroup, String imei) {
        if (CollectionUtil.removeMapKey(chatGroup.getDeviceMap(), imei)) {
            if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
                //更新
                UpdateVO update = new UpdateVO();
                update.setMap("d", chatGroup.getDeviceMap());
                this.updateById(new ObjectId(chatGroup.getId()), update);
            } else {
                this.delById(new ObjectId(chatGroup.getId()));
            }
        }
    }

    /**
     * @param deviceG
     * @param accountId
     * @time: 2020/11/13 14:23
     * @author: Rock
     * @version 1.0
     */
    public ChatGroup getChatGroupByDeviceG(Map<String, Map<String, Object>> deviceG, String accountId) {
        //NOTE 目前的业务为一个设备一个圈子
        if (CollectionUtil.isNotEmpty(deviceG)) {
            for (Map.Entry<String, Map<String, Object>> entry : deviceG.entrySet()) {
                if (StringUtil.isNotBlank(entry.getKey())) {
                    return findById(new ObjectId(entry.getKey()));
                }
            }
        }
        //没有找到则新建
        return createByAccountId(accountId);
    }

    /**
     * 创建聊天群组
     *
     * @param vo
     * @time: 2020/12/18 16:02
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap createChatGroup(BaseAccountReqVO vo) {
        Account account = accountService.findById(vo.getOpenid());
        if (account == null) {
            log.error("Account is null. id={}. createChatGroup vo={}", vo.getOpenid(), vo);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        ChatGroup chatGroup = createByAccountId(vo.getOpenid());
        if (account.getChatGroupMap() == null) {
            account.setChatGroupMap(new HashMap<>());
        }
        accountService.addChatGroup(account, chatGroup.getId());
        ResponseMap res = ResponseMap.success();
        res.put("groupid", chatGroup.getId());
        return res;
    }

    /**
     * 获取聊天群组
     *
     * @param groupid
     * @param vo
     * @time: 2020/12/21 10:04
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getInfo(String groupid, ChatGroupGetInfoReqVO vo) {
        if (!ObjectId.isValid(groupid)) {
            return ResponseMap.fail();
        }
        ChatGroup chatGroup = this.findById(new ObjectId(groupid));
        if (chatGroup == null) {
            log.error("ChatGroup is null. id={}", groupid);
            return ResponseMap.fail();
        }
        List<Map<String, Object>> memberList = new ArrayList<>();
        List<Map<String, Object>> deviceList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            List<Account> accountList = accountService.listByIds(chatGroup.getMemberMap().keySet());
            for (Account account : accountList) {
                Map<String, Object> temp = new HashMap<>();
                temp.put("openid", account.getId());
                temp.put("name", account.getName());
                temp.put("phone", account.getPhone());
                temp.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH
                        + (StringUtil.isBlank(account.getImage()) ? Const.DEFAULT_JPG : account.getImage()));
                memberList.add(temp);
            }
        }
        if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
            for (String key : chatGroup.getDeviceMap().keySet()) {
                Device device = deviceService.findById(key);
                if (device == null) {
                    continue;
                }
                Map<String, Object> temp = new HashMap<>();
                temp.put("imei", device.getId());
                temp.put("vendor", device.getVendor());
                temp.put("name", deviceService.getDeviceName(device));
                temp.put("phone", device.getPhone() == null ? "" : device.getPhone());
                temp.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                        + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
                deviceList.add(temp);
            }
        }

        ResponseMap res = ResponseMap.success();
        res.put("groupid", chatGroup.getId());
        res.put("creator", chatGroup.getCreator());
        res.put("members", memberList);
        res.put("devices", deviceList);
        res.put("avator", "");
        res.put("name", "");
        Document document = accountService.findDocById(vo.getOpenid());
        //检查用户是否是会员,是会员跟不是会员未读条数会有所不同
        VipMember vipMember = vipMemberService.queryVipMemberByOpenid(vo.getOpenid());
        if(ObjectUtils.isEmpty(vipMember)){
            if (document != null && document.get(Constant.NO_VIP_UNREAD_TIME_PREFIX+chatGroup.getId()) != null) {
                res.put("msgNum", document.get(Constant.NO_VIP_UNREAD_TIME_PREFIX+chatGroup.getId()));
            } else {
                res.put("msgNum", 0);
            }
        } else {
            if (document != null && document.get(chatGroup.getId()) != null) {
                res.put("msgNum", document.get(chatGroup.getId()));
            } else {
                res.put("msgNum", 0);
            }
        }

        //log.info(" chatGroup openid = {}. groupid = {}. msgNum = {}", vo.getOpenid(), groupid, res.get("msgNum"));
        res.put("groupType", vo.getGroupType());
        return res;
    }

    /**
     * 修改聊天群信息
     *
     * @param groupid
     * @param vo
     * @param image
     * @time: 2020/12/21 10:03
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap updateChatGroup(String groupid, ChatGroupUpdateReqVO vo, MultipartFile image) throws IOException {
//        ObjectId objId = new ObjectId(groupid);
//        ChatGroup chatGroup = this.findById(objId);
//        if (chatGroup == null) {
//            log.error("ChatGroup is null. id={}", groupid);
//            return ResponseMap.fail();
//        }
//        ResponseMap res = ResponseMap.success();
//        Update update = new Update();
//        if (StringUtil.isNotBlank(vo.getName())) {
//            update.set("name", vo.getName());
//        }
//        if (image != null && !image.isEmpty()) {
//            if (StringUtil.isNotBlank(chatGroup.getImage()) && !chatGroup.getImage().contains("default.jpg")) {
//                //删除旧文件
//                baseBucketTool.delFile(BaseBucketTool.CHATGROUP_COMMON_PATH + chatGroup.getImage());
//            }
//            //生成文件名并上传
//            String fileName = BaseBucketTool.createFileNameToJpg();
//            baseBucketTool.upload(BaseBucketTool.CHATGROUP_COMMON_PATH + fileName, image, 1);
//            update.set("image", fileName);
//            res.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.CHATGROUP_COMMON_PATH + fileName);
//        }
//        this.updateById(objId, update);
//        return res;
        return ResponseMap.fail();
    }

    /**
     * 删除群聊
     *
     * @param groupid
     * @param openid
     * @time: 2020/12/21 10:47
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap del(String groupid, String openid) {
        ObjectId objId = new ObjectId(groupid);
        ChatGroup chatGroup = this.findById(objId);
        if (chatGroup == null) {
            log.error("ChatGroup is null. id={}", groupid);
            return ResponseMap.fail();
        }
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            accountService.delChatGroup(groupid, chatGroup.getMemberMap().keySet());
        }
        if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
            deviceService.delChatGroup(groupid, chatGroup.getDeviceMap().keySet());
        }
        Device device = null;
        Set<String> deviceMap = chatGroup.getDeviceMap().keySet();
        for (String item: deviceMap) {
            if(15 == item.length()){
                device = deviceService.findById(item);
                break;
            }
        }

        //刪除
        this.delById(objId);
        //发送kafka
        JSONObject push_CHAT_GROUP_DISBAND_extOb = new JSONObject();
        push_CHAT_GROUP_DISBAND_extOb.put("operator", openid);
        push_CHAT_GROUP_DISBAND_extOb.put("openid", openid);
        push_CHAT_GROUP_DISBAND_extOb.put("disbandTime", DateUtil.nowTimestampToLong());
        String deviceName = deviceService.getDeviceName(device);
        // 手表【%s】的管理员将手表恢复出厂设置
        String title = MessageUtils.getLocale("API.pushTitle.adminResetDevNotification", deviceName);
        log.info("-----------------method nama ChatGroupService.del,title----------------:"+title);
        push_CHAT_GROUP_DISBAND_extOb.put("msgTitle", title);
        push_CHAT_GROUP_DISBAND_extOb.put("name", deviceName);
        push_CHAT_GROUP_DISBAND_extOb.put("imei", device.getId());
        kafkaService.sendPushToChatGroup(chatGroup, null, ConstKafka.CHAT_GROUP_DISBAND, openid, push_CHAT_GROUP_DISBAND_extOb);
        return ResponseMap.success();
    }

    /**
     * @param groupid
     * @param vo
     * @time: 2020/12/23 15:45
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getInviteToken(String groupid, ChannelParamReqVO vo) {
        ChatGroup chatGroup = null;
        if (ObjectId.isValid(groupid)) {
            chatGroup = this.findById(new ObjectId(groupid));
        } else {
            log.error("getInviteToken groupid={}. vo={}", groupid, vo);
            if (groupid.contains("imei:")) {
                Device device = deviceService.findById(groupid.replace("[imei:", "").replace("]", ""));
                if (device != null && CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
                    for (String key : device.getChatGroupMap().keySet()) {
                        if (ObjectId.isValid(key)) {
                            chatGroup = this.findById(new ObjectId(key));
                            break;
                        }
                    }
                }
            }
        }
        if (chatGroup == null) {
            log.error("ChatGroup is null. id={}", groupid);
            return ResponseMap.fail();
        }
//        if (!listDataProperties.getChannel().contains(vo.getChannel())) {
//            vo.setChannel(listDataProperties.getChannel().get(0));
//        }
        ResponseMap res = ResponseMap.success();
        res.put("inviteToken", chatGroup.getIt());
        String channel = vo.getChannel();
        if (channel == null) {
            channel = "tx";
        }
        //res.put("baseURL", String.format("http://%s/getway/download_%s", domainName, channel));
        //
        res.put("baseURL", String.format("https://%s/index.html", domainName));
//        res.put("baseURL", appDownloadURL);
        return res;
    }

    /**
     * 批量邀请聊天群成员
     *
     * @param groupid
     * @param vo
     * @time: 2020/12/23 17:08
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap batchAddChatGroupAccount(String groupid, MembersOpenIdParamReqVO vo) {
//        ObjectId objId = new ObjectId(groupid);
//        ChatGroup chatGroup = this.findById(objId);
//        if (chatGroup == null) {
//            log.error("ChatGroup is null. id={}", groupid);
//            return ResponseMap.fail();
//        }
//        List<String> addScuess = new ArrayList<>();
//        List<String> addFailure = new ArrayList<>();
//        UpdateVO updateChatGroup = new UpdateVO();
//        JSONArray idArr = JSON.parseArray(vo.getMembersOpenid());
//        for (int i = 0; i < idArr.size(); i++) {
//            Account account = accountService.findById(idArr.getString(i));
//            if (account.getChatGroupMap() == null) {
//                account.setChatGroupMap(new HashMap<>());
//            }
//            if (account.getChatGroupMap().containsKey(groupid)) {
//                addFailure.add(account.getId());
//            } else {
//                addAccountHandle(chatGroup, account.getId(), updateChatGroup);
//                //发送kafka
//                kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId());
//                accountService.addChatGroup(account, groupid);
//                addScuess.add(account.getId());
//            }
//        }
//        //更新
//        this.updateById(objId, updateChatGroup);
//        ResponseMap res = ResponseMap.success();
//        res.put("addScuess", addScuess);
//        res.put("addFailure", addFailure);
//        return res;
        return ResponseMap.fail();
    }

    /**
     * 为群组加上成员 (Account)
     * @param chatGroup
     * @param accountId
     * @time: 2020/12/23 17:03
     * @author: Rock
     * @version 1.0
     */
    public void addAccountHandle(ChatGroup chatGroup, String accountId, UpdateVO updateChatGroup) {
        if (chatGroup.getMemberMap() == null) {
            chatGroup.setMemberMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", System.currentTimeMillis() / 1000d);
        chatGroup.getMemberMap().put(accountId, sub);
        updateChatGroup.setMap("m", chatGroup.getMemberMap());
    }

    /**
     * 邀请聊天群成员
     *
     * @param groupid
     * @param memberOpenid
     * @param vo
     * @time: 2020/12/23 17:35
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap addChatGroupAccount(String groupid, String memberOpenid, ChatGroupAddAccountReqVO vo) {
//        Account account = accountService.findById(memberOpenid);
//        if (account == null) {
//            log.error("Account is null. id={}, aid={}. vo={}", groupid, memberOpenid, vo);
//            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
//        }
//        ObjectId objId = new ObjectId(groupid);
//        ChatGroup chatGroup = this.findById(objId);
//        if (chatGroup == null) {
//            log.error("ChatGroup is null. id={}, aid={} ", groupid, memberOpenid);
//            return ResponseMap.fail(ResponseEnum.STATUS2003.getCode(), ResponseEnum.STATUS2003.getMsg());
//        }
//        if (!vo.getInviteToken().equals(chatGroup.getIt())) {
//            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "圈子邀请码不正确");
//        }
//        accountService.addChatGroup(account, groupid);
//        UpdateVO updateChatGroup = new UpdateVO();
//        addAccountHandle(chatGroup, vo.getOpenid(), updateChatGroup);
//        this.updateById(objId, updateChatGroup);
//        //发送kafka
//        kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId());
//        return ResponseMap.success();
        return ResponseMap.fail();
    }

    /**
     * @param groupid
     * @param memberOpenid
     * @param vo
     * @time: 2020/12/23 17:49
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap delChatGroupAccount(String groupid, String memberOpenid, BaseAccountReqVO vo) {
//        Account account = accountService.findById(memberOpenid);
//        if (account == null) {
//            log.error("Account is null. id={}, aid={}. vo={} delChatGroupAccount ", groupid, memberOpenid, vo);
//            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
//        }
//        ChatGroup chatGroup = this.findById(new ObjectId(groupid));
//        if (chatGroup == null || account == null) {
//            log.error("ChatGroup is null. id={}, aid={} ", groupid, memberOpenid);
//            return ResponseMap.fail(ResponseEnum.STATUS2003.getCode(), ResponseEnum.STATUS2003.getMsg());
//        }
//        UpdateVO updateChatGroup = new UpdateVO();
//        UpdateVO updateAccount = new UpdateVO();
//        if (CollectionUtil.removeMapKey(chatGroup.getMemberMap(), memberOpenid)
//                && CollectionUtil.removeMapKey(account.getChatGroupMap(), groupid)) {
//            updateChatGroup.setMap("m", chatGroup.getMemberMap());
//            updateAccount.setMap("g", account.getChatGroupMap());
//            updateAccount.unset(groupid);
//            accountService.updateById(memberOpenid, updateAccount);
//            this.updateById(new ObjectId(groupid), updateChatGroup);
//            //发送kafk
//            kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_REMOVE, account.getId());
//        } else {
//            return ResponseMap.fail(ResponseEnum.STATUS4003.getCode(), memberOpenid + "不在该群组中");
//        }
//        return ResponseMap.success();
        return ResponseMap.fail();
    }

    /**
     * 发送聊天群聊天消息
     *
     * @param groupid
     * @param openid 发送人openid
     * @param type
     * @param duration
     * @param imei
     * @param content
     * @param bqId
     * @time: 2021/3/6 17:50
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap sendMessage(String groupid, String openid, Integer type, Double duration, String imei, Object content) throws IOException {
        Account account = accountService.findById(openid);
        if (account == null) {
            log.error("Account is null. id={}, aid={}. ", groupid, openid);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        ObjectId objId = new ObjectId(groupid);
        ChatGroup chatGroup = this.findById(objId);
        if (chatGroup == null) {
            log.error("ChatGroup is null. id={}, aid={} ", groupid, openid);
            return ResponseMap.fail(ResponseEnum.STATUS2003.getCode(), ResponseEnum.STATUS2003.getMsg());
        }
        if (!CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
            return ResponseMap.fail();
        }
        ChatGroupMessage message = new ChatGroupMessage();
        message.setGroupid(objId);
        message.setSender(openid);
        message.setAvator(baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH
                + (StringUtil.isBlank(account.getImage()) ? Const.DEFAULT_JPG : account.getImage()));
        message.setMessageType(type);
        message.setSendType(1);
        //能下发的记录都是没有超过限制
        message.setIsExceedLimitRecord(1);
        message.setChatReportTime(DateUtil.stampToDateStr(System.currentTimeMillis(),DateUtil.FORMAT_1));
        //
        JSONObject subJson = new JSONObject();

        String imeiGroup = chatGroup.getDeviceMap().keySet().toArray()[0].toString();
        Device device = deviceService.findById(imeiGroup);
        Object relationship = "爸爸";
        Object relationshipImageId = 1;
        if (device != null && CollectionUtil.containsKey(device.getUserMap(), openid)) {
            relationship = device.getUserMap().get(openid).get("relationship");
            relationshipImageId = device.getUserMap().get(openid).get("relationship_image_id");
            if (StringUtil.isNotBlank(relationship)) {
                message.setRelationship(relationship.toString());
                subJson.put("relationship", message.getRelationship());
            }
            if (StringUtil.isNumeric(relationshipImageId)) {
                message.setRelationshipImageId(Integer.parseInt(relationshipImageId.toString()));
                subJson.put("relationship_image_id", message.getRelationshipImageId());
            }
            message.setOpenid(openid);
            subJson.put("openid", openid);
        }
        if (Const.INT_1.equals(type) && content instanceof String) {
            //文字消息
            //检测敏感词====
            //todo 加了openid参数
            boolean res = contentSecurityUtil.chatTextAudit((String) content,openid);
            if(!res){
                //聊天内容包含敏感词汇
                userKeyActionService.add(10,openid, "用户微聊: 敏感词违规 内容:"+content);
                //保存敏感信息
                sensitivityFileGroupMessageService.saveData(groupid,message);
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "聊天内容包含敏感词汇");
            }
            //检测敏感词====
            message.setContent((String) content);
            subJson.put("content", message.getContent());
        } else if (new Integer(2).equals(type) && content instanceof MultipartFile) {
            MultipartFile file = (MultipartFile) content;
            //图片消息
            if (file.isEmpty()) {
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.request.sendErrorTry");
            }
            //生成文件名并上传
            String fileName = BaseBucketTool.createFileNameToJpg();
            baseBucketTool.upload(BaseBucketTool.MESSAGE_COMMON_PATH + fileName, file, 1);
            message.setSize(file.getSize());
            message.setContent(fileName);
            subJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + fileName);
        } else if (new Integer(3).equals(type) && content instanceof MultipartFile) {
            //语音
            MultipartFile file = (MultipartFile) content;
            if (file.isEmpty() || StringUtil.isBlank(type)) {
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.request.sendErrorTry");
            }
            //生成文件名并上传
            String fileName = BaseBucketTool.createFileName(file, "amr");
            baseBucketTool.upload(BaseBucketTool.MESSAGE_COMMON_PATH + fileName, file, 2);
            message.setSize(file.getSize());
            message.setDuration(duration);
            message.setContent(fileName);
            subJson.put("content", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + fileName);
            subJson.put("duration", duration > 1 ? duration : 1);
        } else if(type == 4){
            //表情包
            message.setContent(content.toString());
            message.setBqId(content.toString());
            subJson.put("bqId",content.toString());
        } else {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "params type is invalid " + type);
        }
        if (new Integer(3).equals(type)) {
            //检测敏感词-语音====
            if(!contentSecurityUtil.chatAudioAudit(subJson.getString("content") ,openid,groupid,message.getId())) {
                log.error("发送微聊的语音信息包含敏感信息，无法发送，出现敏感信息的设备为:{},所属群组为：{},发送的用户为：{}",imei,groupid,openid);
                //保存敏感信息
                sensitivityFileGroupMessageService.saveData(groupid,message);
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "发送语音包含敏感信息");
            }
        } else if(new Integer(2).equals(type)) {
            if(!ContentSecurityUtil.imageAudit(subJson.getString("content"),openid)){
                log.error("发送微聊的图片信息包含敏感信息，无法发送，出现敏感信息的设备为:{},所属群组为：{},发送的用户为：{}",imei,groupid,openid);
                //保存敏感信息
                sensitivityFileGroupMessageService.saveData(groupid,message);
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "发送图片包含敏感信息");
            }
        }
        //保存消息
        chatGroupMessageService.add(message);
        //在用户表中 累加群未读标识
        if (CollectionUtil.isNotEmpty(chatGroup.getMemberMap())) {
            FindIterable<Document> accountList = accountService.listDocByIds(chatGroup.getMemberMap().keySet());
            for (Document doc : accountList) {
                String tempOpenid = doc.getString("_id");
                if (openid.equals(tempOpenid)) {
                    //自己不累加未读标识
                    continue;
                }
                Update updateAcount = new Update();
                int messageNum = doc.getInteger(groupid) == null ? 0 : doc.getInteger(groupid);
                updateAcount.set(groupid, ++messageNum);
                //更新群组未读消息
                Document docG = doc.get("g", Document.class);
                //非vip的消息条数
                Integer noVipNum =  ObjectUtils.isEmpty(doc.get(Constant.NO_VIP_UNREAD_TIME_PREFIX+groupid))?0:doc.getInteger(Constant.NO_VIP_UNREAD_TIME_PREFIX+groupid)+1;
                if (docG != null) {
                    int badge = 0;
                    for (String key : docG.keySet()) {
                        badge += doc.getInteger(key) == null ? 0 : doc.getInteger(key);
                    }
                    updateAcount.set(Constant.NO_VIP_UNREAD_TIME_PREFIX+groupid,noVipNum);
                    updateAcount.set("badge", ++badge);
                    accountService.updateById(tempOpenid, updateAcount);
                }
            }
        }
        //更新操作
        accountService.updateById(openid, new Update().set(groupid, 0));
        subJson.put("id", message.getId());
        subJson.put("groupType", 0);
        subJson.put("senderID", message.getSender());
        subJson.put("senderType", message.getSendType());
        subJson.put("type", message.getMessageType());
        Double timestamp = message.getTimestamp();
        Long timestampLong ;
        if(timestamp == null){
            timestampLong = DateUtil.nowTimestampToLong();
        }else{
            timestampLong = timestamp.longValue();
        }
        subJson.put("timestamp", timestampLong);
        //发送kafka
        JSONObject pushData = new JSONObject();
        pushData.put("type", ConstKafka.CHAT_GROUP_MESSAGE);
        pushData.put("groupid", groupid);
        pushData.put("message", subJson);
        //发送推送
        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_1, groupid, pushData);
        //下发到设备的 指令
        JSONObject json = new JSONObject();
        json.put("groupId", groupid);
        json.put("groupType", 0);
        json.put("contentType", message.getMessageType());
        json.put("timestamp", timestampLong);
        json.put("time", timestampLong);
        json.put("imei", imei);
        //附带上和手表的关系
        json.put("relationship_image_id",relationshipImageId);
        json.put("relationship",relationship);
        if (Const.INT_1.equals(message.getMessageType())) {
            json.put("id", message.getId());
            //TODO: 判断设备是 ASR设备, 如果文字超过 230个字节,直接截断到230个
            String contentStr = message.getContent();
            if(device != null && "ASR".equalsIgnoreCase(device.getPlatform())){
                int maxByteLen = 230;
                if (contentStr.getBytes().length > maxByteLen) {
                    return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.request.sendMsgErrorTooLong");
//                    log.info("发送设备imei:{} 的文字信息 38 指令的内容超过 {} 个字节,截取内容到限制长度再发送到设备", imei, maxByteLen);
//                    byte[] bs = new byte[maxByteLen];
//                    System.arraycopy(contentStr.getBytes(), 0, bs, 0, maxByteLen);
//                    contentStr = new String(bs, "UTF-8");
                }
            }
            json.put("content", contentStr);
            json.put("type", ConstKafka.DEVICE_TYPE_ChatMessage);
        } else if (new Integer(2).equals(message.getMessageType()) ){
            //图片
            json.put("id", message.getId());
            json.put("photoUrl", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.getContent());
            //在OSS 上面的文件路径
            json.put("photoPath", "/"+BaseBucketTool.MESSAGE_COMMON_PATH + message.getContent());
            json.put("size", message.getSize() == null ? 0 : message.getSize());
            json.put("type", ConstKafka.DEVICE_TYPE_DownChatVoice);
        } else if(new Integer(3).equals(message.getMessageType())) {
            //语音
            json.put("id", message.getId());
            json.put("voiceUrl", baseBucketTool.getDOMAIN() + BaseBucketTool.MESSAGE_COMMON_PATH + message.getContent());
            //在OSS 上面的文件路径
            json.put("voicePath", "/"+BaseBucketTool.MESSAGE_COMMON_PATH + message.getContent());
            json.put("duration", message.getDuration() == null ? 0 : message.getDuration().intValue()+1);
            json.put("size", message.getSize() == null ? 0 : message.getSize());
            json.put("type", ConstKafka.DEVICE_TYPE_DownChatVoice);
        } else if(new Integer(4).equals(message.getMessageType())){
            json.put("id", message.getId());
            //json.put("bqId", bqId);
            json.put("content",content.toString());
            json.put("type",ConstKafka.DEVICE_TYPE_ChatMessage);
        }
        if (json.size() > 0) {
            if (StringUtil.isBlank(account.getWxgzIdent()) || Const.INT_0.equals(account.getWxgzIdent())) {
                for (Map.Entry<String, Map<String, Object>> entry : chatGroup.getDeviceMap().entrySet()) {
                    if (StringUtil.isNotBlank(entry.getValue().get("v")) && String.valueOf(Const.VENDER_20000).equals(entry.getValue().get("v").toString())) {
                        //检查设备是否为新版微聊协议，是新版则组装新版微聊下发的指令数据，否则按旧版协议去推送数据
                        //json = assembleNewWechatData(type, entry.getKey(), json, account.getPhone(),"1");
                        json = assemblyNewWechatData(type, entry.getKey(), json, account.getId(), "1");
                        kafkaService.emitDeviceDemand(entry.getKey(), json.getString("type"), json, openid);
                        //设置用户今日发送消息的次数
                        settingUserTodayChatSendTimes(openid);
                    }
                }
            } else {
                if (StringUtil.isNotBlank(imei)) {
                    //检查设备是否为新版微聊协议，是新版则组装新版微聊下发的指令数据，否则按旧版协议去推送数据
                    //json = assembleNewWechatData(type, imei, json, account.getPhone(),"1");
                    json = assemblyNewWechatData(type, imei, json, account.getId(), "1");
                    kafkaService.emitDeviceDemand(imei, json.getString("type"), json, openid);
                    //设置用户今日发送消息的次数
                    settingUserTodayChatSendTimes(openid);
                }
            }
        }
        ResponseMap res = ResponseMap.success();
        res.put("id", message.getId());
        res.put("content", subJson.get("content"));
        return res;
    }

    /**
     * @description 组装新版微聊下发的指令数据
     * @author cwj
     * @param [type, entry, json, account]
     * @return void
     * @date @time 2025/6/27 18:15
     **/
    public JSONObject assembleNewWechatData(Integer type,String imei, JSONObject json, String phone,String x) {
        Device dev = deviceService.findById(imei);
        if(!ObjectUtils.isEmpty(dev.getIsNewWechatProtocol()) && dev.getIsNewWechatProtocol().equals(1)) {
            switch(type) {
                case 1:
                case 4:
                {
                    json.put("type", ConstKafka.DEVICE_NEW_APP_TEXT_MSG);
                    break;
                }
                case 2:{
                    json.put("type", ConstKafka.DEVICE_NEW_APP_IMAGE_MSG);
                    break;
                }
                case 3:{
                    json.put("type", ConstKafka.DEVICE_NEW_APP_VOICE_MSG);
                    break;
                }
            }
            json.put("phone", phone);
            json.put("x",x);
            json.put("currentTime",new Date().getTime()/1000);
        }
        return json;
    }

    public JSONObject assemblyNewWechatData(Integer type,String imei, JSONObject json, String openId,String x) {
        Device dev = deviceService.findById(imei);
        if(!ObjectUtils.isEmpty(dev.getIsNewWechatProtocol()) && dev.getIsNewWechatProtocol().equals(1)) {
            switch(type) {
                case 1:
                case 4:
                {
                    json.put("type", ConstKafka.DEVICE_NEW_APP_TEXT_MSG);
                    break;
                }
                case 2:{
                    json.put("type", ConstKafka.DEVICE_NEW_APP_IMAGE_MSG);
                    break;
                }
                case 3:{
                    json.put("type", ConstKafka.DEVICE_NEW_APP_VOICE_MSG);
                    break;
                }
            }
            json.put("openId", openId);
            json.put("x",x);
            json.put("currentTime",new Date().getTime()/1000);
        }
        return json;
    }

    /**
     * @description 设置用户今日已经免费下发微聊信息的次数
     * @author cwj
     * @param [openid]
     * @return void
     * @date @time 2025/1/10 9:11
     **/
    private void settingUserTodayChatSendTimes(String openid) {
        int userTodayChatUpTimes = getTodaySendTimes(openid);
        String todayStr = DateUtil.dateToStr(new Date(),DateUtil.FORMAT_4);
        userTodayChatUpTimes++;
        redisFilterService.setex(openid+"-" + todayStr + Constant.REDIS_VIP_MEMBER_POSTFIX,24*60*60,String.valueOf(userTodayChatUpTimes));
    }

    /**
     * 获取聊天群聊天消息
     *
     * @param groupid
     * @param vo
     * @time: 2021/1/14 11:40
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getMessage(String groupid, ChatGroupGetMessageReqVO vo, HttpServletRequest request) {
        Document account = accountService.findDocById(vo.getOpenid());
        if (account == null) {
            log.error("Account is null. id={}. getMessage vo={}", vo.getOpenid(), vo);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        Update updateAccount = new Update();
        updateAccount.set(groupid, 0);
        updateAccount.set(Constant.NO_VIP_UNREAD_TIME_PREFIX+groupid,0);
        Double deletetime = 0d;
        int badge = 0;
        Document groupDoc = account.get("g", Document.class);
        if (groupDoc != null) {
            for (Map.Entry<String, Object> entry : groupDoc.entrySet()) {
                if (groupid.equals(entry.getKey()) && entry.getValue() instanceof Document) {
                    if (((Document) entry.getValue()).containsKey("deletetime")) {
                        deletetime = Double.parseDouble(((Document) entry.getValue()).get("deletetime").toString());
                    } else {
                        deletetime = Double.parseDouble(((Document) entry.getValue()).get("t").toString());
                    }
                }
                if (account.getInteger(entry.getKey()) != null) {
                    badge += account.getInteger(entry.getKey());
                }
            }
            updateAccount.set("badge", badge);
            accountService.updateById(vo.getOpenid(), updateAccount);
        }
        ResponseMap res = ResponseMap.success();
        Document query = new Document();
        //参数query处理
        ParamHandle.queryParamHandle(query, vo.getQuery());
        if (deletetime == null) {
            deletetime = 0d;
        }
        query.put("i", new ObjectId(groupid));
        //检查用户是否是会员，不是会员时发送消息的内容是否超过限制，会员可以查看超过限制的记录，非会员则只能看没有超过限制的记录
        VipMember vipMember = vipMemberService.queryVipMemberByOpenid(vo.getOpenid());
        if(ObjectUtils.isEmpty(vipMember)){
            query.put("isExceedLimitRecord",1);
        }
//       log.info("群聊获取聊天消息的 mongo 查询命令 query:{}", query);
        //判断是否是 iOS + 微信
        boolean isWX_ios = checkIsWX_IOS(request);
        res.put("messages", chatGroupMessageService.messageQueryHandle(deletetime, query, vo.getNum(), vo.getPage(), vo.getSort(), isWX_ios));
//        log.info("res:{}", res);
        return res;
    }


    /**
     * @description 查询群聊消息最后一条记录
     * @author cwj
     * @param [groupid, openid]
     * @return com.iot08.common.utils.response.ResponseMap
     * @date @time 2025/7/1 11:19
     **/
    public ResponseMap getLastMessage(String groupid,String openid){
        Document account = accountService.findDocById(openid);
        if (account == null) {
            log.error("Account is null. id={}", openid);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        Double deletetime = 0d;
        Document groupDoc = account.get("g", Document.class);
        if (groupDoc != null) {
            for (Map.Entry<String, Object> entry : groupDoc.entrySet()) {
                if (groupid.equals(entry.getKey()) && entry.getValue() instanceof Document) {
                    if (((Document) entry.getValue()).containsKey("deletetime")) {
                        deletetime = Double.parseDouble(((Document) entry.getValue()).get("deletetime").toString());
                    } else {
                        deletetime = Double.parseDouble(((Document) entry.getValue()).get("t").toString());
                    }
                }
            }
        }
        //查询最后一条记录
        ChatMessageLastResp chatMessageLastResp = chatGroupMessageService.queryLastMessage(groupid,openid,deletetime);
        ResponseMap res = ResponseMap.success();
        res.put("chatMessageLastResp",ObjectUtils.isEmpty(chatMessageLastResp)?new ChatMessageLastResp():chatMessageLastResp);
        return res;
    }
    /**
     * 清空聊天群聊天消息
     *
     * @param groupid
     * @param vo
     * @time: 2021/2/22 10:05
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap delMessage(String groupid, ChatGroupDelAccountReqVO vo) {
        Account account = accountService.findById(vo.getOpenid());
        if (account == null) {
            log.error("Account is null. id={}. delMessage vo={}", vo.getOpenid(), vo);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        if (CollectionUtil.isNotEmpty(account.getChatGroupMap())) {
            Update updateAccount = new Update();
            for (Map.Entry<String, Map<String, Object>> entry : account.getChatGroupMap().entrySet()) {
                if (entry.getKey().equals(groupid)) {
                    entry.getValue().put("deletemsgid", vo.getDeletemsgid());
                    entry.getValue().put("deletetime", DateUtil.nowTimestampToLong());
                }
            }
            updateAccount.set("g", account.getChatGroupMap());
            accountService.updateById(vo.getOpenid(), updateAccount);
        }
        return ResponseMap.success();
    }

    /**
     * @description 获取限制信息
     * @author cwj
     * @param []
     * @return int
     * @date @time 2025/1/9 17:40
     **/
    public int getUserChatUpLimit() {
        //默认上限为50次
        int userChatUpLimit = 100;
        //检查今天有聊天次数有没有到上限
        String userChatUpLimitStr = redisFilterService.get(Constant.USER_SEND_VOICE_TIMES_UP_LIMIT_REDIS_KEY);
        if(StringUtils.isEmpty(userChatUpLimitStr)){
            //查询上限数据库
            SystemLimit systemLimit = systemLimitService.querySystemLimit();
            if(!ObjectUtils.isEmpty(systemLimit)){
                userChatUpLimit = systemLimit.getUserSendVoiceTimesUpLimit();
                redisFilterService.set(Constant.USER_SEND_VOICE_TIMES_UP_LIMIT_REDIS_KEY,String.valueOf(userChatUpLimit));
            }
        } else {
            userChatUpLimit = Integer.parseInt(userChatUpLimitStr);
        }
        return userChatUpLimit;
    }

    /**
     * @description 获取今天已经发送的微聊信息次数
     * @author cwj
     * @param [openid]
     * @return int
     * @date @time 2025/1/9 17:42
     **/
    public int getTodaySendTimes(Object openid) {
        int todaySendTimes = 0;
        String todayStr = DateUtil.dateToStr(new Date(),DateUtil.FORMAT_4);
        String todaySendTimeStr = redisFilterService.get(openid+"-" + todayStr + Constant.REDIS_VIP_MEMBER_POSTFIX);
        if(!StringUtils.isEmpty(todaySendTimeStr)){
            todaySendTimes = Integer.parseInt(todaySendTimeStr);
        }
        return todaySendTimes;
    }

    public boolean checkIsWX_IOS(HttpServletRequest req){
        /*

            # 安卓手机, 微信小程序 请求头
            user-agent : Mozilla/5.0 (Linux; Android 14; 23113RKC6C Build/UKQ1.230804.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 XWEB/1160083 MMWEBSDK/20231202 MMWEBID/8242 MicroMessenger/8.0.47.2560(0x28002F51) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android

            # iOS手机, 微信小程序 请求头
            user-agent : Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.37(0x1800252f) NetType/WIFI Language/zh_CN

            */


        //判断是否 iOS + 微信小程序
        //获取不区分大小写, 统一小写
        String userAgent = req.getHeader("user-agent");
        boolean isWX_IOS = false;
        if(userAgent.contains("iPhone") && userAgent.contains("MicroMessenger")){
            log.info("识别为 iOS + 微信 (目前无法分辨到小程序)  userAgent:{}", userAgent);
            //iOS + 微信 (目前无法分辨到小程序)
            isWX_IOS = true;
        }
        return isWX_IOS;
    }
}
